﻿//-----------------------------------------------------------------------------
// File: RuntimeInfo.cs
// Author: Pierre Biermann (tauron031@gmail.com)
// Project: FFMPEG audio video converter class library
// Namespace(s): FFShellAPI.FFMPEG.Core
//
// Desc: Class for getting runtime information during convertion progress
//       
// (c) 2007 - 2013 by Pierre Biermann
//-----------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Timers;

namespace FFShellAPI.FFMPEG.Core
{
    /// <summary>
    /// Class that provides several rumtime data during an encoding process.
    /// </summary>
    public class RuntimeInfo : OutputParser
    {
        Timer sTimer;
        double _deltaTime;
        double _lastTime;
        double _currentTime;

        /// <summary>
        /// Creates a new RuntimeInfo instance.
        /// </summary>
        public RuntimeInfo()
        {
            CurrentTime = new TimeSpan();
            sTimer = new Timer(5000d);
            sTimer.Elapsed += new ElapsedEventHandler(sTimer_Elapsed);
            sTimer.Enabled = false;
            _deltaTime = 0d;
            _lastTime = 0d;
            _currentTime = 0d;
        }

        private void sTimer_Elapsed(object sender, ElapsedEventArgs e)
        {
            _deltaTime = _currentTime - _lastTime;
            _lastTime = _currentTime;
        }

        /// <summary>
        /// This function is always called when the property RawData has been changed.
        /// </summary>
        protected virtual void UpdateRuntimeInfo()
        {
            int intVal = 0;
            double doubleVal = 0d;
            TimeSpan time = new TimeSpan();
            if (ExtractTime(ref time))
            {
                //_deltaTime = time - CurrentTime;
                CurrentTime = time;
            }
            if (ExtractDuration(ref time))
            {
                if (Duration.TotalSeconds <= 0d)
                    Duration = time;
            }
            intVal = ExtractFrame();
            if (intVal >= 0)
                CurrentFrame = intVal;
            intVal = ExtractFramesPerSecond();
            if (intVal >= 0)
                FramesPerSecond = intVal;
            intVal = ExtractSize();
            if (intVal >= 0)
                ByteSize = intVal;
            doubleVal = ExtractCurrentBitrate();
            if (doubleVal >= 0)
                CurrentBitrate = doubleVal;
        }

        private static double SecondsFromTimeSpan(TimeSpan time)
        {
            double dtime = time.Hours * 3600d + time.Minutes * 60d + time.Seconds;
            return dtime;
        }

        /// <summary>
        /// Returns a string with runtime informations.
        /// </summary>
        /// <returns>info. string</returns>
        public override string ToString()
        {
            return String.Format("frame: {0}; bitrate: {1:0}; fps: {2}; {3}kB ({4:0.00}%)",
                CurrentFrame, CurrentBitrate, FramesPerSecond, ByteSize, Progress * 100d);
        }

        /// <summary>
        /// Gets or sets the encoder process data.
        /// </summary>
        public override string RawData
        {
            get
            {
                return base.RawData;
            }
            set
            {
                base.RawData = value;
                UpdateRuntimeInfo();
            }
        }

        /// <summary>
        /// Returns the total length in seconds.
        /// </summary>
        public double Length
        {
            get { return SecondsFromTimeSpan(Duration); }
        }

        /// <summary>
        /// Returns the current encoding position in seconds.
        /// </summary>
        public double Position
        {
            get { return SecondsFromTimeSpan(CurrentTime); }
        }

        /// <summary>
        /// Returns the convertion progress
        /// </summary>
        public double Progress
        {
            get
            {
                double currentTime = SecondsFromTimeSpan(CurrentTime);
                double duration = SecondsFromTimeSpan(Duration);
                if (currentTime > 0d)
                {
                    double progress = currentTime / duration;
                    return progress;
                }
                return 0d;
            }
        }

        /// <summary>
        /// Returns the remaining time span to complete the convertion process
        /// </summary>
        public TimeSpan RemainingTime
        {
            get
            {
                double currentTime = SecondsFromTimeSpan(CurrentTime);
                double duration = SecondsFromTimeSpan(Duration);
                if (_deltaTime > 0)
                {
                    double remainingTime = (duration - currentTime) / _deltaTime * sTimer.Interval / 1000d;
                    return TimeSpan.FromSeconds(remainingTime);
                }
                else
                {
                    return TimeSpan.Zero;
                }
            }
        }

        /// <summary>
        /// Switch the timer that is used to measure the remainig convertion time on or off.
        /// </summary>
        public bool TimerEnabled
        {
            get { return sTimer.Enabled; }
            set { sTimer.Enabled = value; }
        }

        /// <summary>
        /// Return the bitrate for the frame that is currently handeled by the encoder.
        /// </summary>
        public double CurrentBitrate { get; protected set; }

        /// <summary>
        /// Return the current convertion time.
        /// </summary>
        public TimeSpan CurrentTime
        {
            get { return TimeSpan.FromSeconds(_currentTime); }
            protected set
            {
                _currentTime = SecondsFromTimeSpan(value);
            }
        }

        /// <summary>
        /// Current the total time duration.
        /// </summary>
        public TimeSpan Duration { get; set; }

        /// <summary>
        /// Return the frame number that is currently handeled by the encoder.
        /// </summary>
        public int CurrentFrame { get; protected set; }

        /// <summary>
        /// Returns the value for frames per second that is currently handled by the encoder during encoding process.
        /// </summary>
        public int FramesPerSecond { get; protected set; }

        /// <summary>
        /// Returns the byte size of the output file at this time.
        /// </summary>
        public int ByteSize { get; protected set; }
    }
}
